using System;
using System.Collections.Generic;
using System.Linq;
using FamilyTreeProject.Collections;
using FamilyTreeProject.Data;
using FamilyTreeProject.Common;

namespace FamilyTreeProject
{
    /// <summary>
    /// The IndividualsService provides a Facade to the Individuals Repository,
    /// allowing for additional business logic to be applied.
    /// </summary>
    public class IndividualsService : IIndividualsService
    {
        #region Private Members

        private IRepository<Individual> repository;

        #endregion

        #region Constructors

        /// <summary>
        /// Constructs an Individuals Service that will use the specified
        /// <see cref="IRepository<Individual>"/> to retrieve data
        /// </summary>
        /// <param name="repository">The <see cref="IRepository<Individual>"/> to use to retrieve data</param>
        public IndividualsService(IRepository<Individual> repository)
        {
            //Contract
            Requires.ArgumentNotNull<IRepository<Individual>>(repository);

            this.repository = repository;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds an individual to the data store and sets the <see cref="Individual.Id"/> property
        /// of <paramref name="individual"/> to the id of the new individual.
        /// </summary>
        /// <param name="individual">The individual to add to the data store.</param>
        public void AddIndividual(Individual individual)
        {
            //Contract
            Requires.ArgumentNotNull<Individual>(individual);

            repository.Add(individual);
        }

        /// <summary>
        /// Deletes an individual from the data store
        /// </summary>
        /// <remarks>
        /// The delete operation takes effect immediately
        /// </remarks>
        /// <param name="individual">The individual to delete</param>
        public void DeleteIndividual(Individual individual)
        {
            //Contract
            Requires.ArgumentNotNull<Individual>(individual);

            repository.Delete(individual);
        }

        /// <summary>
        /// Retrieves all the children of an Individual
        /// </summary>
        /// <param name="parentId">The Id of the Parent</param>
        /// <returns>An <see cref="IList{Individual}"/>.</returns>
        public IList<Individual> GetChildren(int parentId)
        {
            //Contract
            Requires.ArgumentNotNegative(parentId);

            return repository.GetAll()
                                .Where(ind => ind.FatherId == parentId || ind.MotherId == parentId)
                                .ToList();
        }

        /// <summary>
        /// Retrieves all the children of a Couple
        /// </summary>
        /// <param name="fatherId">The Id of the Father</param>
        /// <param name="motherId">The Id of the Mother</param>
        /// <returns>An <see cref="IList{Individual}"/>.</returns>
        public IList<Individual> GetChildren(int fatherId, int motherId)
        {
            //Contract
            Requires.ArgumentNotNegative(fatherId);
            Requires.ArgumentNotNegative(motherId);

            return repository.GetAll()
                                .Where(ind => ind.FatherId == fatherId && ind.MotherId == motherId)
                                .ToList();
        }

        /// <summary>
        /// Retrieves a single individual
        /// </summary>
        /// <param name="id">The Id of the Indiidual to retrieve</param>
        /// <param name="includeChildren">A flag that indicates whether to get the children of the Individual</param>
        /// <returns>An <see cref="Individual"/></returns>
        public Individual GetIndividual(int id, bool includeChildren)
        {
            //Contract
            Requires.ArgumentNotNegative(id);

            var individual = repository.GetAll()
                                .WithId(id)
                                .SingleOrDefault();

            if (individual != null && includeChildren)
                individual.Children = GetChildren(id);

            return individual;
        }

        /// <summary>
        /// Retrieves all the Individuals in a Tree
        /// </summary>
        /// <param name="treeId">The Id of the Tree</param>
        /// <returns>An <see cref="IList{Individual}"/>.</returns>
        public IList<Individual> GetIndividuals(int treeId)
        {
            //Contract
            Requires.ArgumentNotNegative(treeId);

            return repository.Find(ind => ind.TreeId == treeId)
                                .ToList();
        }

        /// <summary>
        /// Retrieves a single page of Individuals in a Tree
        /// </summary>
        /// <param name="treeId">The Id of the Tree</param>
        /// <param name="pageNumber">The page number (0-indexed)</param>
        /// <param name="pageSize">The size of the page</param>
        /// <returns>A <see cref="IPagedList{Individual}"/>s.</returns>
        public IPagedList<Individual> GetIndividuals(int treeId, int pageNumber, int pageSize)
        {
            //Contract
            Requires.ArgumentNotNegative(treeId);
            Requires.ArgumentNotNegative(pageNumber);
            Requires.ArgumentNotNegative(pageSize);

            return repository.Find(ind => ind.TreeId == treeId)
                                .InPagesOf(pageSize)
                                .GetPage(pageNumber);
        }

        /// <summary>
        /// Updates an individual in the data store.
        /// </summary>
        /// <param name="individual">The individual to update in the data store.</param>
        public void UpdateIndividual(Individual individual)
        {
            //Contract
            Requires.ArgumentNotNull<Individual>(individual);

            repository.Update(individual);
        }

        #endregion
    }
}
